package cn.harry.modular.system.service.impl;

import cn.harry.common.constant.CommonConstant;
import cn.harry.common.model.KeyValue;
import cn.harry.common.model.Option;
import cn.harry.component.security.utils.SecurityUtils;
import cn.harry.modular.system.domain.SysMenu;
import cn.harry.modular.system.enums.MenuTypeEnums;
import cn.harry.modular.system.enums.StatusEnums;
import cn.harry.modular.system.mapper.SysMenuMapper;
import cn.harry.modular.system.service.SysMenuService;
import cn.harry.modular.system.vo.RouterVo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author harry
 * @公众号 Harry技术
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Override
    public List<SysMenu> getRouters(Long userId) {
        List<SysMenu> menus;
        if (SecurityUtils.isRoot()) {
            menus = list(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getType,
                    Arrays.asList(
                            MenuTypeEnums.CATALOG.getValue(),
                            MenuTypeEnums.MENU.getValue(),
                            MenuTypeEnums.EX_LINK.getValue()
                    )));
        } else {
            menus = this.baseMapper.getRoutersByUserId(userId);
        }
        return getChildPerms(menus, 0);
    }

    @Override
    public List<RouterVo> buildMenus(List<SysMenu> menus) {
        List<RouterVo> routers = new LinkedList<>();
        for (SysMenu menu : menus) {
            RouterVo router = new RouterVo();
            //路由名称
            router.setName(StringUtils.capitalize(menu.getPath()));
            //根据path路由跳转 this.$router.push({path:xxx})
            router.setPath(getRouterPath(menu));
            // 组件地址
            router.setComponent(StringUtils.isEmpty(menu.getUri()) ? "Layout" : menu.getUri());

            RouterVo.MetaVo meta = new RouterVo.MetaVo();
            meta.setTitle(menu.getName());
            meta.setIcon(menu.getIcon());
            meta.setHidden(StatusEnums.DISABLE.getKey().equals(menu.getStatus()));

            // 【菜单】是否开启页面缓存
            if (MenuTypeEnums.MENU.getValue().equals(menu.getType()) && ObjectUtil.equals(menu.getKeepAlive(), 1)) {
                meta.setKeepAlive(true);
            }
            meta.setAlwaysShow(ObjectUtil.equals(menu.getAlwaysShow(), 1));

            List<KeyValue> paramsJson = menu.getParams();
            // 将 JSON 字符串转换为 Map<String, String>
            if (CollectionUtil.isNotEmpty(paramsJson)) {
                try {
                    Map<String, Object> params = BeanUtil.beanToMap(paramsJson);
                    meta.setParams(params);
                } catch (Exception e) {
                    throw new RuntimeException("解析参数失败", e);
                }
            }
            router.setMeta(meta);

            List<SysMenu> cMenus = menu.getChildren();
            if (CollectionUtil.isNotEmpty(cMenus)) {
                router.setChildren(buildMenus(cMenus));
            }
            routers.add(router);
        }
        return routers;
    }

    @Override
    public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {
        List<SysMenu> menuList;
        // 管理员显示所有菜单信息
        if (SecurityUtils.isRoot()) {
            menuList = list();
        } else {
            menuList = this.baseMapper.getPermissionList(userId);
        }

        // 获取所有菜单ID
        Set<Long> menuIds = menuList.stream().map(SysMenu::getId).collect(Collectors.toSet());
        // 获取所有父级ID
        Set<Long> parentIds = menuList.stream().map(SysMenu::getPid).collect(Collectors.toSet());
        // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
        List<Long> rootIds = parentIds.stream().filter(id -> !menuIds.contains(id)).toList();

        // 使用递归函数来构建菜单树
        return rootIds.stream().flatMap(rootId -> buildMenuTree(rootId, menuList).stream()).collect(Collectors.toList());

    }

    @Override
    public List<Option> listMenuOptions(boolean onlyParent) {
        List<SysMenu> menuList = this.list(new LambdaQueryWrapper<SysMenu>().in(onlyParent, SysMenu::getType, MenuTypeEnums.CATALOG.getValue(), MenuTypeEnums.MENU.getValue()).orderByAsc(SysMenu::getSort));
        return buildMenuOptions(CommonConstant.ROOT_NODE_ID, menuList);
    }

    @Override
    public boolean saveMenu(SysMenu sysMenu) {
        Integer menuType = sysMenu.getType();
        boolean result = this.saveOrUpdate(sysMenu);
        // 编辑刷新角色权限缓存
        // 修改菜单如果有子菜单，则更新子菜单的树路径
        return result;
    }

    /**
     * 递归生成菜单下拉层级列表
     *
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return 菜单下拉列表
     */
    private List<Option> buildMenuOptions(Long parentId, List<SysMenu> menuList) {
        List<Option> menuOptions = new ArrayList<>();

        for (SysMenu menu : menuList) {
            if (menu.getPid().equals(parentId)) {
                Option option = new Option(menu.getId(), menu.getName());
                List<Option> subMenuOptions = buildMenuOptions(menu.getId(), menuList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                menuOptions.add(option);
            }
        }

        return menuOptions;
    }

    private List<SysMenu> buildMenuTree(Long pid, List<SysMenu> menuList) {
        return CollectionUtil.emptyIfNull(menuList).stream().filter(menu -> menu.getPid().equals(pid)).map(entity -> {
            SysMenu item = BeanUtil.copyProperties(entity, SysMenu.class);
            List<SysMenu> children = buildMenuTree(entity.getId(), menuList);
            item.setChildren(children);
            return item;
        }).toList();
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
        List<SysMenu> returnList = new ArrayList<>();
        for (SysMenu t : list) {
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getPid() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<SysMenu> list, SysMenu t) {
        // 得到子节点列表
        List<SysMenu> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenu tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                for (SysMenu n : childList) {
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
        List<SysMenu> tlist = new ArrayList<>();
        for (SysMenu n : list) {
            if (n.getPid().longValue() == t.getId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenu> list, SysMenu t) {
        return !getChildList(list, t).isEmpty();
    }

    /**
     * 获取路由地址
     *
     * @param menu 菜单信息
     * @return 路由地址
     */
    public String getRouterPath(SysMenu menu) {
        String routerPath = menu.getPath();
        // 非外链并且是一级目录
        if (0 == menu.getPid() && MenuTypeEnums.CATALOG.getValue().equals(menu.getType())) {
            routerPath = "/" + menu.getPath();
        }
        return routerPath;
    }

}




